package com.fastloan.core.apiauth.base.service.impl.bd;

import com.fastloan.common.functioninterface.Try;
import com.fastloan.core.apiauth.base.anno.encrypt.EncryptMode;
import com.fastloan.core.apiauth.base.anno.sign.SignMode;
import com.fastloan.core.apiauth.base.anno.sign.noSign;
import com.fastloan.core.apiauth.base.endecrypt.EnDecrypt;
import com.fastloan.core.apiauth.base.endecrypt.impl.AesCBC;
import com.fastloan.core.apiauth.base.endecrypt.impl.AesECB;
import com.fastloan.core.apiauth.base.enums.EncryptModeEnum;
import com.fastloan.core.apiauth.base.factory.EncryptModeFactory;
import com.fastloan.core.apiauth.base.service.GeneralEncrypt;
import org.apache.commons.beanutils.BeanUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 百度模式加密
 *
 * @author yanwe
 * createTime 2018-01-2018/1/4 10:55
 */
public class BDEncryptMode extends GeneralEncrypt {

    @Override
    public String signBean(Object object, String key) {
        SignMode signMode = object.getClass().getAnnotation(SignMode.class);
        if (signMode == null){
            return null;
        }
        EncryptModeEnum encryptMode = signMode.encryptMode();
        Function<String, String> singFunction = EncryptModeFactory.getSignFunction(encryptMode);
        Map<String, String> sortBeanMap = Arrays.stream(object.getClass().getDeclaredFields())
                .filter(field -> !field.isAnnotationPresent(noSign.class))//过滤非签名属性
                .sorted(Comparator.comparing(Field::getName))//升序
                .collect(Collectors.toMap(Field::getName
                        , Try.of((Field field) -> BeanUtils.getProperty(object, field.getName())),
                        (oldValue, newValue) -> oldValue, LinkedHashMap::new));
        String concatBean = sortBeanMap.entrySet().stream().map(entry -> entry.getKey() + "=" + entry.getValue())
                .collect(Collectors.joining("&"));
        return singFunction.apply(concatBean + "&key=" + getKey());
    }

    @Override
    public String encryptBean(Object object, String key) {
        String sign = signBean(object,key);
        String encodeBean = Arrays.stream(object.getClass().getDeclaredFields()).map(Try.of(field -> {
            String fieldName = field.getName();
            EncryptMode encryptMode = field.getAnnotation(EncryptMode.class);
            if (encryptMode == null) {
                return BeanUtils.getProperty(object, fieldName);
            }
            String encodeField = encodeField(object, field, encryptMode.sourceEncryptIndex()
                    ,BeanUtils.getProperty(object, fieldName));
            return fieldName + "=" + encodeField;
        })).collect(Collectors.joining("&"));
        return encodeBean+"&sign="+sign;
    }

    @Override
    public String decodeBean(String data) {
        return null;
    }

    private String encodeField(Object object, Field field,int[] sourceEncryptIndex,String encodeValue) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        EncryptMode encryptMode = field.getAnnotation(EncryptMode.class);
        if (encryptMode == null){
            return BeanUtils.getProperty(object, field.getName());
        }
        String source = encryptMode.source();
        if (!"".equals(source)){
            sourceEncryptIndex = encryptMode.sourceEncryptIndex();
            Field sourceField = object.getClass().getDeclaredField(source);
            encodeValue = encodeField(object, sourceField, sourceEncryptIndex, encodeValue);
        }
        Class<? extends EnDecrypt>[] modes = encryptMode.mode();
        List<EnDecrypt> enDecrypts = Arrays.stream(modes).map(Try.of(this::getEncryptMode)).collect(Collectors.toList());
        if (sourceEncryptIndex != null && sourceEncryptIndex.length != 0){//指定加密顺序
            for (int aSourceEncryptIndex : sourceEncryptIndex) {
                encodeValue = enDecrypts.get(aSourceEncryptIndex).encrypt(encodeValue);
            }
        } else {//默认加密顺序
            for (EnDecrypt enDecrypt : enDecrypts) {
                encodeValue = enDecrypt.encrypt(encodeValue);
            }
        }
        return encodeValue;
    }

    private EnDecrypt getEncryptMode(Class<? extends EnDecrypt> encryptMode) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
        if (encryptMode.equals(AesCBC.class)){
            Constructor<AesCBC> constructor = AesCBC.class.getConstructor(String.class, String.class);
            return constructor.newInstance(getAesKey(), getAesIv());
        } else if (encryptMode.equals(AesECB.class)){
            Constructor<AesECB> constructor = AesECB.class.getConstructor(String.class);
            return constructor.newInstance(getAesKey());
        }
        throw new RuntimeException("未知的加密类型:"+encryptMode);
    }
}
